GARCH

ANLY-560 Time Series

Return to the Home page

© 2023, hosted by GU domain

ARCH/GARCH

1 Introduction on Return and Volatility

  • Return: A return is the change in price of an asset, investment, or project over time, which may be represented in terms of price change or percentage change. A positive return represents a profit while a negative return marks a loss.

  • Volatility: Volatility is a statistical measure of the dispersion of returns for a given security. Volatility is often measured as either the standard devidation or variance between returns from that same security or market index. Volatility is an important factor in options trading. Here volatility means the conditional standard deviation of the underlying asset return.

Here we will be modeling and forecast the volatility of future returns on SPY and plot it with the market breadth data; modeling the volatility of an asset return. The models are referred to as conditional heteroscedastic models.

2 SPY return and market breadth visualization

Code
spy <- getSymbols("SPY",auto.assign = FALSE, from = "2020-04-07", to = "2023-04-06",src="yahoo")
spy=data.frame(spy)
spy <- data.frame(spy,rownames(spy))

colnames(spy)[7] = "date"
spy$date<-as.Date(spy$date,"%Y-%m-%d")
spx_breadth_data <- read_csv("spx_market_breadth.csv") %>%
  mutate(Date = ymd(Date)) %>%
  rename(Breadth = `0`)

spy_breadth <- spx_breadth_data %>%
  dplyr::full_join(spy, by = c("Date" = "date"))
Code
# Create the plot
fig <- plot_ly() %>%
  add_trace(
    data = spy_breadth,
    x = ~Date,
    y = ~SPY.Adjusted,
    type = "scatter",
    mode = "lines",
    name = "SPY Adjusted",
    line = list(color = "#1a99c7"),
    yaxis = "y1"
  ) %>%
  add_trace(
    data = spy_breadth,
    x = ~Date,
    y = ~Breadth,
    type = "scatter",
    mode = "lines",
    name = "Breadth",
    line = list(color = "#fcba03"),
    yaxis = "y2"
  ) %>%
  layout(
    title = "SPY and Breadth",
    yaxis = list(
      title = "SPY Adjusted",
      side = "left",
      showgrid = FALSE
    ),
    yaxis2 = list(
      title = "Breadth",
      side = "right",
      overlaying = "y",
      showgrid = FALSE
    ),
    xaxis = list(
      title = "Date"
    )
  )

# Display the plot
fig
Code
spy_ts = ts(spy_breadth$`SPY.Adjusted`, start=decimal_date(as.Date("2020-04-06")), frequency = 365.25)

####### calculating Returns
returns = log(spy_ts) %>% diff()
autoplot(returns) +ggtitle("SPY Returns Since 2020-04-06")

3 Model comparision

3.1 Checking for stationary

Code
require(gridExtra)
p1 = ggAcf(returns,40)+ggtitle("ACF of the SPY return")
p2 = ggPacf(returns,40)+ggtitle("PACF of the SPY return")

grid.arrange(p1,p2, nrow=2)

From the ACF plot we can claim that the return of SPY is weakly stationary.

3.2 Check for correlations

Code
p3 = ggAcf(abs(returns),40)
p4 = ggAcf(returns^2,40)

grid.arrange(p3,p4, nrow=2)

We can see clear correlation in both plots. This correlation may indicate the presence of two types of dependencies in the time series data:

  • Volatility clustering: A significant correlation in the ACF plot of the absolute returns suggests that there is some dependency in the magnitude of returns over time. In other words, large returns (either positive or negative) tend to be followed by more large returns, while small returns tend to be followed by more small returns. This phenomenon is known as volatility clustering and is a common feature in financial time series data.

  • Autoregressive conditional heteroskedasticity (ARCH) effects: A significant correlation in the ACF plot of the squared returns indicates that there is some dependency in the squared returns over time. This suggests the presence of ARCH effects, meaning that the conditional variance of the returns changes over time, with periods of high volatility followed by periods of low volatility and vice versa. ARCH effects can be modeled using an ARCH or GARCH model, which can help improve the accuracy of forecasts.

4 Garch Model Fitting

4.1 Arch Test

We can check for ARCH effects by using the ArchTest() function from the FinTS package. We will use a significance level of \(\alpha\)=0.05 for our null hypothesis test.

Code
library(FinTS)
ArchTest(returns, lags=1, demean=TRUE)

    ARCH LM-test; Null hypothesis: no ARCH effects

data:  returns
Chi-squared = 2.7468, df = 1, p-value = 0.09745

Because the p-value is >0.05, we can’t reject the null hypothesis and conclude there are no presence of ARCH(1) effects.

4.2 Fitting an ARIMA model

Code
spy_returns = log(spy_ts)
auto.arima(spy_returns)
Series: spy_returns 
ARIMA(3,2,0) 

Coefficients:
          ar1      ar2      ar3
      -0.7514  -0.4876  -0.2420
s.e.   0.0354   0.0412   0.0355

sigma^2 = 0.000195:  log likelihood = 2149.12
AIC=-4290.24   AICc=-4290.19   BIC=-4271.75

Thus the p,q value will be use are (3,2).

4.3 Fitting a Garch model

Code
arima.fit<-Arima(spy_returns,order=c(3,2,0))
arima.res<-arima.fit$residuals
arima.res <- na.omit(arima.res)

acf(arima.res^2)

Code
pacf(arima.res^2)

Code
model <- list() ## set counter
cc <- 1
for (p in 1:7) {
  for (q in 1:7) {
  
    model[[cc]] <- garch(arima.res, order=c(p, q), trace=F)
    cc <- cc + 1
  }
} 

## get AIC values for model evaluation
GARCH_AIC <- sapply(model, AIC) ## model with lowest AIC is the best
best_model_index <- which(GARCH_AIC == min(GARCH_AIC))
best_model <- model[[best_model_index]]

p <- (best_model_index - 1) %% 7 + 1
q <- floor((best_model_index - 1) / 7) + 1

cat("Best model:", "GARCH(", p, ",", q, ")\n", sep="")
Best model:GARCH(1,1)
Code
summary(final.fit <- garchFit(~garch(1,1), arima.res, trace = F))

Title:
 GARCH Modelling 

Call:
 garchFit(formula = ~garch(1, 1), data = arima.res, trace = F) 

Mean and Variance Equation:
 data ~ garch(1, 1)
<environment: 0x7fad33c559f0>
 [data = arima.res]

Conditional Distribution:
 norm 

Coefficient(s):
         mu        omega       alpha1        beta1  
-3.0445e-04   7.3250e-06   1.7224e-01   8.0119e-01  

Std. Errors:
 based on Hessian 

Error Analysis:
         Estimate  Std. Error  t value Pr(>|t|)    
mu     -3.045e-04   3.795e-04   -0.802 0.422415    
omega   7.325e-06   2.722e-06    2.691 0.007131 ** 
alpha1  1.722e-01   4.714e-02    3.654 0.000258 ***
beta1   8.012e-01   4.698e-02   17.053  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Log Likelihood:
 2210.366    normalized:  2.927637 

Description:
 Mon May  1 01:05:05 2023 by user:  


Standardised Residuals Tests:
                                Statistic p-Value     
 Jarque-Bera Test   R    Chi^2  68.22681  1.554312e-15
 Shapiro-Wilk Test  R    W      0.9852954 6.978349e-07
 Ljung-Box Test     R    Q(10)  67.5494   1.314957e-10
 Ljung-Box Test     R    Q(15)  72.36932  1.684649e-09
 Ljung-Box Test     R    Q(20)  84.37574  7.012601e-10
 Ljung-Box Test     R^2  Q(10)  14.53093  0.1501222   
 Ljung-Box Test     R^2  Q(15)  16.38849  0.3567127   
 Ljung-Box Test     R^2  Q(20)  17.01533  0.6519783   
 LM Arch Test       R    TR^2   16.37455  0.1746768   

Information Criterion Statistics:
      AIC       BIC       SIC      HQIC 
-5.844678 -5.820165 -5.844733 -5.835235 

5 Prediction and correlation

Code
garch_predict= predict(final.fit, n.ahead = 100, plot = TRUE)

Code
ht <- final.fit@h.t #a numeric vector with the conditional variances (h.t = sigma.t^delta)
# Prepare the data
data <- data.frame(ht = ht, Date = spy_breadth$Date[-1], Breadth = spy_breadth$Breadth[-1])

# Normalize the ht and Breadth series
data$ht_normalized <- (data$ht - min(data$ht)) / (max(data$ht) - min(data$ht))
data$Breadth_normalized <- (data$Breadth - min(data$Breadth)) / (max(data$Breadth) - min(data$Breadth))

# Create the plotly plot
fig <- plot_ly() %>%
  add_lines(data = data, x = ~Date, y = ~ht_normalized, name = "Conditional Variance (Volatality)", line = list(color = "#d64a27")) %>%
  add_lines(data = data, x = ~Date, y = ~Breadth_normalized, name = "Market Breadth", line = list(color = "#fcba03")) %>%
  layout(
    title = "Normalized Conditional Variance and Market Breadth",
    xaxis = list(
      title = "Date",
      rangeslider = list(visible = T)),
    yaxis = list(title = "Normalized Value")
  )

# Display the plot
fig

By plotting the the volatatlity and the market breadth measurement together, it is clear that there are some correlation between the two. When the volatality of the market increase, the market breadth is often meet a spike.

5.1 Model equation and discussion

\[ r_t = -3.0445 \times 10^{-4} + \epsilon_t \] \[ \sigma_t^2 = 7.3251 \times 10^{-6} + 1.7224 \times 10^{-1} \epsilon_{t-1}^2 + 8.0119 \times 10^{-1} \sigma_{t-1}^2 \]

From the fitted GARCH(1,1) model, we can draw several useful insights and observations:

-Persistence of volatility: The estimated coefficients \(\alpha_1\) and \(\beta_1\) are 0.17224 and 0.80119, respectively. The sum of these coefficients is 0.97343, which is close to 1. This indicates a high degree of persistence in volatility, meaning that large changes in returns are likely to be followed by large changes, and small changes are likely to be followed by small changes. This persistence suggests that the volatility in the SPY returns has a strong memory, and past shocks continue to affect future volatility for some time.

-Significance of GARCH terms: Both the \(\alpha_1\) and \(\beta_1\) coefficients are statistically significant, as indicated by their p-values (0.000258 and < 2e-16, respectively). This implies that both past shocks (\(\epsilon_{t-1}^2\)) and past volatility (\(\sigma_{t-1}^2\)) play a significant role in determining current volatility. In other words, the GARCH(1,1) model is suitable for capturing the dynamics of the SPY return series.

-Conditional variance and market breadth: When plotting the normalized conditional variance (ht) and the normalized market breadth, we observed that there might be some correlation between the two series’ trends. This suggests that the market breadth could be a relevant factor in explaining the volatility of the SPY returns. A more in-depth analysis, such as a Granger causality test or a multivariate GARCH model, could provide further insights into the relationship between market breadth and SPY return volatility.

In conclusion, the GARCH(1,1) model offers valuable insights into the volatility dynamics of the SPY return series. The model demonstrates the persistence of volatility and the significance of GARCH terms in capturing these dynamics. Additionally, the potential correlation between market breadth and SPY volatility suggests that market breadth could play a role in explaining the observed return volatility. Further analysis could help uncover the extent of this relationship and its implications for risk management and trading strategies.